/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkRtmpMuxer.h"
#include "MkAmf/MkAmf.h"
#include "MkUtil/MkLog.h"

Uint32 CMkRtmpMuxer::PacketCommandMessage(const Uint32& ChunkSize, const MkString& CmdName, Uint32 TransactionId, const MkVector<MkAmfObject>& VecObj, Uint8* pBuf, Uint32 nLen, CMkBufferList& BufferList)
{
    MkVector<MkAmfObject> SendObjs;
    SendObjs.push_back(MkAmfObject("", MkAmfString, CmdName));
    SendObjs.push_back(MkAmfObject("", MkAmfNumber, static_cast<double>(TransactionId)));
    SendObjs.insert(SendObjs.end(), VecObj.begin(), VecObj.end());
    Uint8* pBegin = pBuf;
    Uint8* pEnd = pBuf + nLen;
    pBegin = CMkAmf::EncodeObject(pBegin, pEnd, SendObjs);
    Uint32 nUsed = pBegin - pBuf;
    BufferList.Append(pBuf, nUsed);
    return PacketRtmpSystemMsgPacket(ChunkSize, RtmpMsgAmf0, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketResponseMessage(const Uint32& ChunkSize, const MkString& StringResult, Uint32 TransactionId, const MkVector<MkAmfObject>& VecObj, Uint8* pBuf, Uint32 nLen, CMkBufferList& BufferList)
{
    MkVector<MkAmfObject> SendObjs;
    SendObjs.push_back(MkAmfObject("", MkAmfString, StringResult));
    SendObjs.push_back(MkAmfObject("", MkAmfNumber, (double)TransactionId));
    SendObjs.insert(SendObjs.end(), VecObj.begin(), VecObj.end());
    Uint8* pBegin = pBuf;
    Uint8* pEnd = pBuf + nLen;
    pBegin = CMkAmf::EncodeObject(pBegin, pEnd, SendObjs);
    Uint32 nUsed = pBegin - pBuf;
    BufferList.Append(pBuf, nUsed);
    /*
        Uint8* pBegin = pBuf;
        Uint8* pEnd = pBuf + nLen;
        pBegin = CMkAmf::EncodeString(pBegin, pEnd, StringResult);
        pBegin = CMkAmf::EncodeNumber(pBegin, pEnd, TransactionId);
        if (MkAmfInvalid != CmdObject.DataType) {
            pBegin = CMkAmf::EncodeOneObject(pBegin, pEnd, CmdObject);
        }
        if (!VecObj.empty()) {
            pBegin = CMkAmf::EncodeBeginObject(pBegin, pEnd);
        }
        MkVector<MkAmfObject>::const_iterator it = VecObj.begin();
        for (; it != VecObj.end(); it++) {
            pBegin = CMkAmf::EncodeOneObject(pBegin, pEnd, *it);
        }
        if (!VecObj.empty()) {
            pBegin = CMkAmf::EncodeEndObject(pBegin, pEnd);
        }
        Uint32 nUsed = pBegin - pBuf;
        BufferList.Append(pBuf, nUsed);*/

    return PacketRtmpSystemMsgPacket(ChunkSize, RtmpMsgAmf0, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketSetChunkSize(const Uint32& SetChunkSize, const Uint32& CurChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 6) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (SetChunkSize >> 24) & 0xFF;
    pBuf[1] = (SetChunkSize >> 16) & 0xFF;
    pBuf[2] = (SetChunkSize >> 8) & 0xFF;
    pBuf[3] = SetChunkSize & 0xFF;
    nUsed = 4;
    BufferList.Append(pBuf, 4);
    return PacketRtmpControlPacket(CurChunkSize, RtmpMsgChunkSize, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketAbortMessage(Uint32 StreamId, const Uint32& ChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 6) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (StreamId >> 24) & 0xFF;
    pBuf[1] = (StreamId >> 16) & 0xFF;
    pBuf[2] = (StreamId >> 8) & 0xFF;
    pBuf[3] = StreamId & 0xFF;
    nUsed = 4;
    BufferList.Append(pBuf, 4);
    return PacketRtmpControlPacket(ChunkSize, RtmpMsgAbort, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketAcknowledgement(Uint32 nSize, const Uint32& ChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 6) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (nSize >> 24) & 0xFF;
    pBuf[1] = (nSize >> 16) & 0xFF;
    pBuf[2] = (nSize >> 8) & 0xFF;
    pBuf[3] = nSize & 0xFF;
    nUsed = 4;
    BufferList.Append(pBuf, 4);
    return PacketRtmpControlPacket(ChunkSize, RtmpMsgAcknowledgement, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketWindownAcknowledgementSize(Uint32 ServerBw, const Uint32& ChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 6) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (ServerBw >> 24) & 0xFF;
    pBuf[1] = (ServerBw >> 16) & 0xFF;
    pBuf[2] = (ServerBw >> 8) & 0xFF;
    pBuf[3] = ServerBw & 0xFF;
    nUsed = 4;
    BufferList.Append(pBuf, 4);
    return PacketRtmpControlPacket(ChunkSize, RtmpMsgWindownAcknowledgementSize, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketPeerBandwidth(Uint32 WinSize, Uint8 LimitType, const Uint32& ChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 7) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (WinSize >> 24) & 0xFF;
    pBuf[1] = (WinSize >> 16) & 0xFF;
    pBuf[2] = (WinSize >> 8) & 0xFF;
    pBuf[3] = WinSize & 0xFF;
    pBuf[4] = LimitType;
    nUsed = 5;
    BufferList.Append(pBuf, 5);
    return PacketRtmpControlPacket(ChunkSize, RtmpMsgSetPeerBandWith, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketStreamBegin(Uint32 StreamId, const Uint32& ChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 6) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (StreamId >> 24) & 0xFF;
    pBuf[1] = (StreamId >> 16) & 0xFF;
    pBuf[2] = (StreamId >> 8) & 0xFF;
    pBuf[3] = StreamId & 0xFF;
    nUsed = 4;
    BufferList.Append(pBuf, 4);
    return PacketUserControlPacket(ChunkSize, ControlEventStreamBegin, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketStreamEof(Uint32 StreamId, const Uint32& ChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 6) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (StreamId >> 24) & 0xFF;
    pBuf[1] = (StreamId >> 16) & 0xFF;
    pBuf[2] = (StreamId >> 8) & 0xFF;
    pBuf[3] = StreamId & 0xFF;
    nUsed = 4;
    BufferList.Append(pBuf, 4);
    return PacketUserControlPacket(ChunkSize, ControlEventStreamEof, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketStreamDry(Uint32 StreamId, const Uint32& ChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 6) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (StreamId >> 24) & 0xFF;
    pBuf[1] = (StreamId >> 16) & 0xFF;
    pBuf[2] = (StreamId >> 8) & 0xFF;
    pBuf[3] = StreamId & 0xFF;
    nUsed = 4;
    BufferList.Append(pBuf, 4);
    return PacketUserControlPacket(ChunkSize, ControlEventStreamDry, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketSetBufferLength(Uint32 StreamId, Uint32 LenPerMilliseconds, const Uint32& ChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 10) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (StreamId >> 24) & 0xFF;
    pBuf[1] = (StreamId >> 16) & 0xFF;
    pBuf[2] = (StreamId >> 8) & 0xFF;
    pBuf[3] = StreamId & 0xFF;
    pBuf[4] = (LenPerMilliseconds >> 24) & 0xFF;
    pBuf[5] = (LenPerMilliseconds >> 16) & 0xFF;
    pBuf[6] = (LenPerMilliseconds >> 8) & 0xFF;
    pBuf[7] = LenPerMilliseconds & 0xFF;
    nUsed = 8;
    BufferList.Append(pBuf, 8);
    return PacketUserControlPacket(ChunkSize, ControlEventSetBufferLen, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketStreamIsRecorded(Uint32 StreamId, const Uint32& ChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 6) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (StreamId >> 24) & 0xFF;
    pBuf[1] = (StreamId >> 16) & 0xFF;
    pBuf[2] = (StreamId >> 8) & 0xFF;
    pBuf[3] = StreamId & 0xFF;
    nUsed = 4;
    BufferList.Append(pBuf, 4);
    return PacketUserControlPacket(ChunkSize, ControlEventStreamIsRecored, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketPingRequest(Uint32 TimeStamp, const Uint32& ChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 6) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (TimeStamp >> 24) & 0xFF;
    pBuf[1] = (TimeStamp >> 16) & 0xFF;
    pBuf[2] = (TimeStamp >> 8) & 0xFF;
    pBuf[3] = TimeStamp & 0xFF;
    nUsed = 4;
    BufferList.Append(pBuf, 4);
    return PacketUserControlPacket(ChunkSize, ControlEventPingRequest, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketPongResponse(Uint32 TimeStamp, const Uint32& ChunkSize, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (nLen < 6) {
        return NotHasEnoughBuffer;
    }
    pBuf[0] = (TimeStamp >> 24) & 0xFF;
    pBuf[1] = (TimeStamp >> 16) & 0xFF;
    pBuf[2] = (TimeStamp >> 8) & 0xFF;
    pBuf[3] = TimeStamp & 0xFF;
    nUsed = 4;
    BufferList.Append(pBuf, 4);
    return PacketUserControlPacket(ChunkSize, ControlEventPingResponse, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketUserControlPacket(const Uint32& ChunkSize, const MkRtmpControlEvent& EventType, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    if (2 + nUsed > nLen) {
        return NotHasEnoughBuffer;
    }
    pBuf[nUsed++] = (EventType >> 8) & 0xFF;
    pBuf[nUsed++] = EventType & 0xFF;
    BufferList.Append(pBuf + nUsed - 2, 2);
    return PacketRtmpControlPacket(ChunkSize, RtmpMsgControlEvent, pBuf, nLen, nUsed, BufferList);
}

Uint32 CMkRtmpMuxer::PacketRtmpControlPacket(const Uint32& ChunkSize, const MkRtmpCommandType& MsgType, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    CMkBufferList RetBufferList;
    Uint32 ErrorCode = PacketRtmpPacket(ChunkSize, MkRtmpHeaderFmt0, MsgType, ChunkControl, 0, pBuf, nLen, nUsed, BufferList, RetBufferList);
    MkCheckErrorLog(ErrorCode, "packet rtmp packet failed\n");
    BufferList = RetBufferList;
    return ErrorCode;
}

Uint32 CMkRtmpMuxer::PacketRtmpSystemMsgPacket(const Uint32& ChunkSize, const MkRtmpCommandType& MsgType, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    CMkBufferList RetBufferList;
    Uint32 ErrorCode = PacketRtmpPacket(ChunkSize, MkRtmpHeaderFmt1, MsgType, ChunkSystem, 0, pBuf, nLen, nUsed, BufferList, RetBufferList);
    MkCheckErrorLog(ErrorCode, "packet rtmp packet failed\n");
    BufferList = RetBufferList;
    return ErrorCode;
}

Uint32 CMkRtmpMuxer::PacketResponseMsgPacket(const Uint32& ChunkSize, const MkRtmpCommandType& MsgType, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    CMkBufferList RetBufferList;
    Uint32 ErrorCode = PacketRtmpPacket(ChunkSize, MkRtmpHeaderFmt0, MsgType, ChunkResponse, 0, pBuf, nLen, nUsed, BufferList, RetBufferList);
    MkCheckErrorLog(ErrorCode, "packet rtmp packet failed\n");
    BufferList = RetBufferList;
    return ErrorCode;
}

Uint32 CMkRtmpMuxer::PacketMediaPacket(const Uint32& ChunkSize, const MkRtmpCommandType& MsgType, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, CMkBufferList& BufferList)
{
    CMkBufferList RetBufferList;
    Uint32 ErrorCode = PacketRtmpPacket(ChunkSize, MkRtmpHeaderFmt0, MsgType, ChunkMedia, 0, pBuf, nLen, nUsed, BufferList, RetBufferList);
    MkCheckErrorLog(ErrorCode, "packet rtmp packet failed\n");
    BufferList = RetBufferList;
    return ErrorCode;
}

Uint32 CMkRtmpMuxer::PacketRtmpPacket(const Uint32& ChunkSize, const MkRtmpHeaderFmtType& FmtType, const MkRtmpCommandType& MsgType, Uint16 ChunkId, Uint32 TimeStamp, Uint8* pBuf, Uint32 nLen, Uint32& nUsed, const CMkBufferList& BufferList, CMkBufferList& OutBufferList)
{
    Uint8 *pBegin = pBuf + nUsed;
    Uint8 *pEnd = pBuf + nLen;
    Uint8 Head = FmtType << 6;
    if (ChunkId < 64) {
        Head |= ChunkId;
        pBegin = CMkAmf::EncodeUint8(pBegin, pEnd, Head);
    } else if (ChunkId < 319) {
        pBegin = CMkAmf::EncodeUint8(pBegin, pEnd, Head);
        pBegin = CMkAmf::EncodeUint8(pBegin, pEnd, (ChunkId - 64) & 0xFF);
    } else {
        Head |= 1;
        pBegin = CMkAmf::EncodeUint8(pBegin, pEnd, Head);
        pBegin = CMkAmf::EncodeUint16(pBegin, pEnd, (ChunkId - 64) & 0xFFFF);
    }

    if (FmtType < MkRtmpHeaderFmt3)  // add time stamp
    {
        pBegin = CMkAmf::EncodeUint24(pBegin, pEnd, TimeStamp > 0xFFFFFF ? 0xFFFFFF : TimeStamp);
    }
    if (FmtType < MkRtmpHeaderFmt2) {
        pBegin = CMkAmf::EncodeUint24(pBegin, pEnd, BufferList.GetTatolLen());  //message len
        pBegin = CMkAmf::EncodeUint8(pBegin, pEnd, MsgType);
    }
    if (FmtType < MkRtmpHeaderFmt1) {
        pBegin = CMkAmf::EncodeUint32(pBegin, pEnd, RtmpDefaultStreamId);
    }

    if (TimeStamp > 0xFFFFFF) {
        pBegin = CMkAmf::EncodeUint32(pBegin, pEnd, TimeStamp);
    }

    Uint8 pFisrtHead = pBuf[nUsed];

    //append chunk header
    OutBufferList.Append(pBuf + nUsed, pBegin - pBuf - nUsed);

    Uint32 BodyLen = BufferList.GetTatolLen();
    Uint32 BodyOffset = 0;

    while (BodyOffset < BodyLen) {
        if (BodyLen > ChunkSize + BodyOffset) {
            CMkBufferList TmpBufferList;
            BufferList.GetBuffer(BodyOffset, ChunkSize, TmpBufferList);
            OutBufferList.Append(TmpBufferList);
            BodyOffset += ChunkSize;
            *pBegin = (0xc0 | pFisrtHead);
            pBegin++;
            if (TimeStamp > 0xFFFFFF) {
                pBegin = CMkAmf::EncodeUint32(pBegin, pEnd, TimeStamp);
                OutBufferList.Append(pBegin - 5, 5);
            } else {
                OutBufferList.Append(pBegin - 1, 1);
            }
        } else {
            CMkBufferList TmpBufferList;
            BufferList.GetBuffer(BodyOffset, BodyLen - BodyOffset, TmpBufferList);
            OutBufferList.Append(TmpBufferList);
            break;
        }
    }
    return NoneError;
}